package at.ltasat;

import java.util.ArrayList;
import java.util.List;

import at.ltasat.classifier.ObjectFeatures;
import at.ltasat.classifier.ObjectFeaturesFactory;
import at.ltasat.util.Average;
import at.tasat.cnf.Clause;
import at.tasat.cnf.Cnf;
import at.tasat.cnf.Literal;

//TODO: refine feature definitions
public final class CnfFeaturesFactory implements ObjectFeaturesFactory<Cnf> {

	public static final CnfFeaturesFactory INSTANCE = new CnfFeaturesFactory();

	private CnfFeaturesFactory() {
		// nothing to do
	}

	@Override
	public int getNumberOfFeatures() {
		return 5;
	}

	@Override
	public ObjectFeatures create(Cnf cnf) {
		final double[] values = new double[getNumberOfFeatures()];
		values[0] = getVariablesToClausesRatio(cnf);
		final Average clauseSize = Average.get(getClauseSizes(cnf));
		values[1] = clauseSize.average() / cnf.getNumberOfVariables();
		values[2] = clauseSize.deviation() / cnf.getNumberOfVariables();
		values[3] = getUnitsToClausesRatio(cnf);
		values[4] = getDominantPhaseToAllLiteralsRatio(cnf);
		return new ObjectFeatures(values);
	}

	private static double getVariablesToClausesRatio(Cnf cnf) {
		return ((double) cnf.getNumberOfVariables()) / cnf.getNumberOfClauses();
	}

	private static Iterable<Integer> getClauseSizes(Cnf cnf) {
		List<Integer> list = new ArrayList<Integer>();
		for (Clause clause : cnf) {
			list.add(Integer.valueOf(clause.getNumberOfLiterals()));
		}
		return list;
	}

	private static double getUnitsToClausesRatio(Cnf cnf) {
		int nUnits = 0;
		for (Clause c : cnf) {
			if (c.getNumberOfLiterals() == 1) {
				nUnits++;
			}
		}
		return ((double) nUnits) / cnf.getNumberOfClauses();
	}

	private static double getDominantPhaseToAllLiteralsRatio(Cnf cnf) {
		int nPositives = 0, nNegatives = 0, nLiterals = 0;
		for (Clause clause : cnf) {
			for (int i = 0; i < clause.getNumberOfLiterals(); i++) {
				nLiterals++;
				if (Literal.getPhase(clause.getLiteral(i))) {
					nPositives++;
				} else {
					nNegatives++;
				}
			}
		}
		return ((double) Math.max(nPositives, nNegatives)) / nLiterals / 2;
	}

}
